﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PServiceBus.Core.Runtime;
using PServiceBus.Core.Logger;

namespace PServiceBus.Gateway.Interfaces {
    internal sealed class ServiceRouter : IDisposable {
        private string _address;
        private bool _disposed;
        private IBusRouter _router;
        private Exception _firstException;

        public ServiceRouter(string address) {
            _address = address;
        }

        private void Initialize(bool isRetry = false) {
            if (isRetry) {
                //Invalidate currently failed router
                var router =
                    ESBGateway.RouterAddresses.FirstOrDefault(
                        r => _address == r.Endpoint);
                if (router != null) 
                    router.IsAvailable = false;
                
                //Retrieve next available router
                var nextAvailableRouter = ESBGateway.RouterAddresses.FirstOrDefault(r => r.IsAvailable);
                if (nextAvailableRouter == null) {
                    if (_firstException != null) {
                        try {
                            throw _firstException;
                        } finally {
                            _firstException = null;
                        }
                    } else
                        throw new Exception("There is no alternative endpoints set for routing request");
                }
                _address = nextAvailableRouter.Endpoint;
            }
            CreateRouter();
        }

        private IBusRouter Router {
            get {
                if (_router == null) CreateRouter();
                return _router;
            }
        }

        private void CreateRouter() {
            if (_router != null) _router.Dispose();
            var busRouter = ServiceLocator<IBusRouter>.Get();
            if (busRouter == null) 
                throw new InvalidOperationException("Could not locate IBusRouter implementation using ServiceLocator, please define it");
            _router = busRouter.Init(_address);
        }

        public void Execute(Action<IBusRouter> action) {
            try {
                action(Router);
            } 
            catch (Exception ex) {
                if (_firstException == null)
                    _firstException = ex;
                if (!ESBGateway.RouterAddresses.Any(x => x.IsAvailable)) 
                    ESBLogger.Log(ex);
                Initialize(isRetry: true);
                Execute(action);
            }
        }

        public void Dispose() {
            if (_disposed) return;
            if (_router != null) _router.Dispose();
            _router = null;
            _disposed = true;
        }
    }
}
